package com.evil.application.enums.order;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.evil.application.enums.form.control.ControlCodeEnum;
import com.evil.application.pojo.dto.form.control.ControlBaseInfoDTO;
import com.evil.application.pojo.entity.ApplicationOrderData;
import com.evil.common.account.dto.user.UserBaseRespDTO;
import com.evil.common.application.dto.form.SpecialControlConfigDTO;
import com.evil.common.application.dto.form.control.ControlBaseDTO;
import com.evil.common.application.dto.order.data.ControlDataDTO;
import com.evil.common.application.dto.order.data.ControlDataValueDTO;
import com.evil.common.application.dto.order.data.NodeOtherInfoDTO;
import com.evil.common.application.dto.order.data.SpecialControlData;
import com.evil.common.application.dto.process.node.NodeBaseDTO;
import com.evil.common.core.enums.RCodeEnum;
import com.evil.common.core.exception.BusinessException;
import com.evil.common.core.util.StreamUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;

import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 订单特殊控件配置
 *
 * @author liyang
 * @date 2022-08-08 00:00
 */
@AllArgsConstructor
public enum OrderSpecialControlEnum {

    /**
     * 订单特殊控件配置
     */
    SN(-1L, ControlCodeEnum.INPUT, "编号", "sn", true, false, true, true,
            special -> StringUtils.defaultIfBlank(special.getSn(), "编号"),
            (applicationOrder, getUserBase) -> {
                return ListUtil.toList(new ControlDataValueDTO(applicationOrder.getOrderSn()));
            }
    ),

    NODE(-2L, ControlCodeEnum.SELECT, "当前流程节点", "node", true, false, true, false,
            special -> StringUtils.defaultIfBlank(special.getNode(), "当前流程节点"),
            (applicationOrder, getUserBase) -> {
                return new ArrayList<>();
            }
    ),

    CREATOR(-3L, ControlCodeEnum.MEMBER_SEL, "申请人", "creator", false, true, false, true,
            special -> StringUtils.defaultIfBlank(special.getCreator(), "申请人"),
            (applicationOrder, getUserBase) -> {
                UserBaseRespDTO creator = getUserBase.apply(applicationOrder.getCreatorId());
                return ListUtil.toList(new ControlDataValueDTO(creator.getUserId(), creator.getNickName()).setOtherInfo(creator));
            }
    ),

    CREATE(-4L, ControlCodeEnum.DATE_PICKER, "创建时间", "create", false, false, false, true,
            special -> StringUtils.defaultIfBlank(special.getCreate(), "创建时间"),
            (applicationOrder, getUserBase) -> {
                return ListUtil.toList(new ControlDataValueDTO(DateUtil.format(applicationOrder.getCreated(), DatePattern.NORM_DATETIME_PATTERN)));
            }
    ),

    MODIFIER(-6L, ControlCodeEnum.MEMBER_SEL, "更新人", "modifier", false, false, true, false,
            special -> StringUtils.defaultIfBlank(special.getModifier(), "更新人"),
            (applicationOrder, getUserBase) -> {
                UserBaseRespDTO modifier = getUserBase.apply(applicationOrder.getModifierId());
                return ListUtil.toList(new ControlDataValueDTO(modifier.getUserId(), modifier.getNickName()).setOtherInfo(modifier));
            }
    ),

    UPDATE(-5L, ControlCodeEnum.DATE_PICKER, "更新时间", "update", false, false, true, false,
            special -> StringUtils.defaultIfBlank(special.getUpdate(), "更新时间"),
            (applicationOrder, getUserBase) -> {
                return ListUtil.toList(new ControlDataValueDTO(DateUtil.format(applicationOrder.getUpdated(), DatePattern.NORM_DATETIME_PATTERN)));
            }
    ),
    ;

    @Getter
    private final Long id;
    @Getter
    private final ControlCodeEnum controlCode;
    @Getter
    private final String name;
    @Getter
    private final String field;
    /**
     * 列表排序是否前置
     */
    @Getter
    private final boolean isFront;
    /**
     * 是否可以导入
     */
    @Getter
    private final boolean canImport;
    /**
     * 是否可以更新值
     */
    @Getter
    private final boolean canUpdate;
    /**
     * 控件是否有控件条件（网关节点时是否可以作为控件条件）
     */
    @Getter
    private final boolean hasCondition;
    /**
     * 自定义特殊控件标题
     */
    @Getter
    private final Function<SpecialControlConfigDTO, String> specialTitle;
    @Getter
    private final BiFunction<SpecialControlData, Function<Long, UserBaseRespDTO>, List<ControlDataValueDTO>> classifier;

    public ControlBaseDTO toControlBase(SpecialControlConfigDTO specialConfigDto) {
        ControlBaseInfoDTO controlBaseInfoDTO = new ControlBaseInfoDTO();
        controlBaseInfoDTO.setControlId(this.id);
        controlBaseInfoDTO.setControlCode(this.controlCode.getCode());
        controlBaseInfoDTO.setControlField(this.field);
        String title = this.specialTitle.apply(specialConfigDto);
        controlBaseInfoDTO.setControlTitle(title);
        controlBaseInfoDTO.setControlTip(title);
        controlBaseInfoDTO.setParentControlId(0L);
        return controlBaseInfoDTO;
    }

    public ControlBaseInfoDTO toControlBaseInfo(SpecialControlConfigDTO specialControlConfigDTO) {
        return new ControlBaseInfoDTO(this.toControlBase(specialControlConfigDTO));
    }

    public ControlDataDTO createControlData(SpecialControlConfigDTO specialConfigDto, SpecialControlData specialControlData, Function<Long, UserBaseRespDTO> getUserBase) {
        ControlDataDTO controlDataDTO = new ControlDataDTO();
        controlDataDTO.setControlId(this.id);
        controlDataDTO.setControlCode(this.controlCode.getCode());
        controlDataDTO.setControlTitle(this.specialTitle.apply(specialConfigDto));
        controlDataDTO.setParentControlId(0L);

        if (null != specialControlData) {
            controlDataDTO.setValues(this.classifier.apply(specialControlData, getUserBase));
        }
        return controlDataDTO;
    }

    public Predicate<ControlDataDTO> hasControlData() {
        return controlData -> this.id.equals(controlData.getControlId());
    }

    public Predicate<ApplicationOrderData> hasOrderData() {
        return orderData -> this.id.equals(orderData.getControlId());
    }

    public void supplyData(SpecialControlConfigDTO specialConfigDto, Collection<ControlDataDTO> controlDataList, SpecialControlData specialControlData, Function<Long, UserBaseRespDTO> getUserBase) {
        if (this.noneControlData(controlDataList)) {
            controlDataList.add(this.createControlData(specialConfigDto, specialControlData, getUserBase));
        }
    }

    public void modifyData(Collection<ControlDataDTO> controlDataList, SpecialControlData specialControlData, Function<Long, UserBaseRespDTO> getUserBase) {
        this.findControlData(controlDataList).setValues(this.classifier.apply(specialControlData, getUserBase));
    }

    public boolean noneControlData(Collection<ControlDataDTO> controlDataList) {
        return controlDataList.stream().noneMatch(this.hasControlData());
    }

    public Optional<ControlDataDTO> isExistControlData(Collection<ControlDataDTO> controlDataList) {
        return controlDataList.stream().filter(this.hasControlData()).findFirst();
    }

    public ControlDataDTO findControlData(Collection<ControlDataDTO> controlDataList) {
        return this.isExistControlData(controlDataList).orElseThrow(() -> new BusinessException(RCodeEnum.ERROR_RUNTIME));
    }

    public Optional<ApplicationOrderData> findOrderData(Collection<ApplicationOrderData> orderDataList) {
        return orderDataList.stream().filter(this.hasOrderData()).findFirst();
    }

    public static void supplyNodeData(SpecialControlConfigDTO specialConfigDto, SpecialControlData specialControlData, Collection<ControlDataDTO> controlDataList, NodeBaseDTO nodeBase) {
        ControlDataDTO controlDataDTO = OrderSpecialControlEnum.NODE.isExistControlData(controlDataList).orElseGet(() -> {
            ControlDataDTO cd = OrderSpecialControlEnum.NODE.createControlData(specialConfigDto, specialControlData, id -> null);
            controlDataList.add(cd);
            return cd;
        });

        ControlDataValueDTO controlDataValueDTO = new ControlDataValueDTO(nodeBase.getNodeId(), nodeBase.getNodeName());
        controlDataValueDTO.setValue(nodeBase.getNodeName());
        controlDataValueDTO.setOtherInfo(new NodeOtherInfoDTO());
        controlDataDTO.setValues(ListUtil.toList(controlDataValueDTO));
    }

    public static List<ControlBaseDTO> supplySpecialControlBases(SpecialControlConfigDTO specialControlConfigDTO, List<ControlBaseDTO> controls, boolean supplySpecialControl) {
        if (supplySpecialControl) {
            OrderSpecialControlEnum.forEach(em -> controls.add(em.toControlBase(specialControlConfigDTO)));
        }
        return controls;
    }

    public static Stream<OrderSpecialControlEnum> stream() {
        return Arrays.stream(values());
    }

    public static List<OrderSpecialControlEnum> getEnums() {
        return Arrays.asList(values());
    }

    public static void forEach(Consumer<OrderSpecialControlEnum> consumer) {
        OrderSpecialControlEnum.getEnums().forEach(consumer);
    }

    public static Optional<OrderSpecialControlEnum> isExistById(Long id) {
        if (null == id) {
            return Optional.empty();
        }
        return Arrays.stream(values()).filter(e -> e.id.compareTo(id) == 0).findFirst();
    }

    public static OrderSpecialControlEnum findById(Long id) {
        return OrderSpecialControlEnum.isExistById(id).orElseThrow(OrderSpecialControlEnum::getBusinessException);
    }

    public static boolean isSpecialControlId(Long id) {
        return OrderSpecialControlEnum.getSpecialControlIds().contains(id);
    }

    public static List<Long> getSpecialControlIds() {
        return OrderSpecialControlEnum.stream().map(OrderSpecialControlEnum::getId).collect(Collectors.toList());
    }

    public static List<Long> getCanUnImportControlIds() {
        return OrderSpecialControlEnum.stream().filter(e -> !e.canImport).map(OrderSpecialControlEnum::getId).collect(Collectors.toList());
    }

    public static List<ControlDataDTO> initControlData(SpecialControlConfigDTO specialControlConfigDTO, SpecialControlData specialControlData, Function<Long, UserBaseRespDTO> getUserBase) {
        return OrderSpecialControlEnum.stream().map(e -> e.createControlData(specialControlConfigDTO, specialControlData, getUserBase)).collect(Collectors.toList());
    }

    /**
     * 补充特殊控件数据
     *
     * @param specialControlConfigDTO specialControlConfigDTO
     * @param controlDataList         controlDataList
     * @return List
     */
    public static List<ControlDataDTO> supplySpecialControlData(SpecialControlConfigDTO specialControlConfigDTO, List<ControlDataDTO> controlDataList) {
        controlDataList.addAll(OrderSpecialControlEnum.stream()
                .map(em -> em.createControlData(specialControlConfigDTO, null, null))
                .collect(Collectors.toList()));
        return controlDataList;
    }

    public static List<ControlBaseInfoDTO> initControlBases(SpecialControlConfigDTO specialControlConfigDTO) {
        return OrderSpecialControlEnum.stream().map(e -> e.toControlBaseInfo(specialControlConfigDTO)).collect(Collectors.toList());
    }

    public static Map<Long, ControlBaseInfoDTO> initControlBaseMap(SpecialControlConfigDTO specialControlConfigDTO) {
        return StreamUtil.toMapK(OrderSpecialControlEnum.initControlBases(specialControlConfigDTO), ControlBaseInfoDTO::getControlId);
    }

    public static BusinessException getBusinessException() {
        return new BusinessException(RCodeEnum.ERROR_PARAMETER, "订单特殊控件配置不存在");
    }

}
